package com.security.core.validate.code;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import com.security.core.properties.SecurityConstants;
import com.security.core.properties.SecurityProperties;

/**
 * @title ValidateCodeFilter
 * @version 1.0.0
 * @description 
 * @author liukang
 * @date 2019年5月15日
 */
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
	
	/**
	 * @description 校验失败处理器
	 */
	@Autowired
	private AuthenticationFailureHandler authenticationFailureHandler;

	/**
	 * @description 系统配置信息
	 */
	@Autowired
	private SecurityProperties securityProperties;
	
	/**
	 * @description 系统中的校验码处理器
	 */
	@Autowired
	private ValidateCodeProcessorHolder validateCodeProcessorHolder;
	
	
	/**
	 * @description 存放所有需要校验的url
	 */
	private Map<String, ValidateCodeType> urlMap = new HashMap<>();
	
	/**
	 * @description 验证请求url与配置的url是否匹配的工具类
	 */
	private AntPathMatcher antPathMatcher = new AntPathMatcher();
	
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
		//初始化添加默认的验证码校验路径
		urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);
		urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
		
		addUrlToMap(securityProperties.getCode().getImage().getUrl(),ValidateCodeType.IMAGE);
		addUrlToMap(securityProperties.getCode().getSms().getUrl(), ValidateCodeType.SMS);
	}
	
	/**
	 * @title addUrlToMap
	 * @description 将系统中配置的需要校验码的验证路径根据校验的类型放入url中
	 * @author Dark_blue
	 * @date 2019年8月27日
	 * @param urlString
	 * @param validateCodeType
	 */
	private void addUrlToMap(String urlString, ValidateCodeType validateCodeType) {
		if (StringUtils.isNotBlank(urlString)) {
			String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(), ",");
			for (String configUrl : configUrls) {
				urlMap.put(configUrl ,validateCodeType);
			}
		}
	}
	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		ValidateCodeType type = getValidateCodeType(request);
		if (type != null) {
			logger.info("校验请求(" + request.getRequestURI() + ")中的验证码,验证码类型" + type);
			try {
				validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(new ServletWebRequest(request));
				logger.info("验证码校验通过");
			} catch (ValidateCodeException e) {
				authenticationFailureHandler.onAuthenticationFailure(request, response, e);
				return ;
			}
		}
		filterChain.doFilter(request, response);
	}

	
	/**
	 * @title getValidateCodeType
	 * @description 判断当前请求需不需要验证，如果需要验证，则返回验证类型
	 * @author Dark_blue
	 * @date 2019年8月27日
	 * @param request
	 * @return
	 */
	private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
		ValidateCodeType result = null;
		if (!StringUtils.equalsIgnoreCase(request.getMethod(), "get")) {
			Set<String> urls = urlMap.keySet();
			for (String url : urls) {
				if (antPathMatcher.match(url, request.getRequestURI())) {
					result = urlMap.get(url);
				}
			}
		}
		return result;
	}

	public AuthenticationFailureHandler getAuthenticationFailureHandler() {
		return authenticationFailureHandler;
	}

	public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
		this.authenticationFailureHandler = authenticationFailureHandler;
	}

	public SecurityProperties getSecurityProperties() {
		return securityProperties;
	}

	public void setSecurityProperties(SecurityProperties securityProperties) {
		this.securityProperties = securityProperties;
	}

}
